#ifndef __SHM_HPP__
#define __SHM_HPP__
#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/lyl/Shm";
const int gproj_id = 0x66;
const int gShmSize = 4096; // 4096*n
class Shm
{
private:
    // 挂接
    void *Attach()
    {
        // 如果已经挂接了，需要先接挂接
        if (_addrshm != nullptr)
        {
            Detch(_addrshm);
        }
        return shmat(_shmid, nullptr, 0);
    }
    // 解挂接
    void Detch(void *address)
    {
        shmdt(address);
    }

public:
    Shm(std::string pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        // 创建公共的key值，是创建共享内存的基础
        _key = GetCommKey();
        // 区分用户
        if (_who == gCreater)
        {
            _shmid = shmget(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0600);
            if (_shmid == -1)
            {
                perror("shmget:");
            }
            std::cout << "shm has created" << std::endl;
        }
        else
        {
            _shmid = shmget(_key, gShmSize, IPC_CREAT | 0600);
            if (_shmid == -1)
            {
                perror("shmget:");
            }
            std::cout << "shm has ready" << std::endl;
        }
        // 将共享内存挂接到地址空间
        _addrshm = Attach();
    }
    ~Shm()
    {
        if (_who == gCreater)
        {
            shmctl(_shmid, IPC_RMID, NULL);
        }
        // 析构的时候自动调用解挂接
        Detch(_addrshm);
    }
    // 创建公共的key值，是创建共享内存的基础
    key_t GetCommKey()
    {
        key_t k = ftok(_pathname.c_str(), _proj_id);
        if (k < 0)
            perror("ftok:");
        return k;
    }
    // 将公共值k，转化为16进制
    std::string ToHex()
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", _key);
        return buffer;
    }
    // 获取共享内存地址
    void *GetAddress()
    {
        return _addrshm;
    }
    // 起始内存全部置为0
    void Zero()
    {
        memset(_addrshm, '\0', gShmSize);
    }

private:
    key_t _key; // 公共k值
    int _shmid; // 共享内存的唯一标识符
    // 生成k值的参数
    std::string _pathname;
    int _proj_id;
    //
    int _who;       // 身份
    void *_addrshm; // 地址空间里共享内存的起始地址
};

#endif